#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

// pub use bollard_next::container::Config;
pub use bollard_next::models::HealthConfig;
pub use bollard_next::models::HostConfig;

use crate::generic::ImagePullPolicy;
pub use cri_api::v1::{ContainerConfig,ImageSpec,KeyValue,Mount};
use cri_api::v1::{LinuxPodSandboxConfig, PodSandboxConfig};
use crate::cri_spec;
use crate::cri_spec::{CriConfigPartial, CriConfig};
/// Auto is used to automatically define that the number of replicas in the cluster
/// Number is used to manually set the number of replicas
/// Note: auto will ensure at least 1 replica exists in the cluster
/*
    定义cargo的副本模式，不同的策略决定副本数量
    Auto：自动决定集群中的副本数量，至少保持 1 个副本。
    Unique：确保集群中只有一个副本。
    UniqueByNode：确保每个节点上运行一个副本。
    UniqueByNodeGroups 和 UniqueByNodeNames：分别确保在指定的节点组或节点名中运行副本。
    Static :手动指定某个节点上运行的副本数量
    StaticByNodes: 手动指定每个节点上运行的副本数量
    StaticByNodeGroups: 手动指定某些特定节点组中的副本数量
    StaticByNodeNames: 手动指定某些特定节点上运行的副本数量
    #[derive(Debug, Clone, PartialEq, Eq)]
      PartialEq：允许对结构体实例进行相等性比较（== 和 !=）。
      Eq：这是 PartialEq 的补充，它要求类型的比较是完全相等的，不能有不确定性。
    #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
      它的意思是 当启用了 utoipa 功能时，为该结构体派生 utoipa::ToSchema trait。这用于自动生成 OpenAPI 文档，
      utoipa 是一个生成 OpenAPI 规范的工具
      utoipa::ToSchema：用于将结构体转换为 OpenAPI 模式，方便生成 API 文档。
    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
      是一个用于生成 JSON Schema 的库，这个派生使结构体能够生成相应的 JSON Schema 定义。
    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
      为结构体派生 Serialize 和 Deserialize trait。

    #[cfg_attr(feature = "serde", serde(deny_unknown_fields, tag = "Mode", rename_all = "PascalCase"))]
      作用：这是 serde 的属性，用于进一步配置序列化和反序列化的行为。这些选项定义了结构体在序列化/反序列化过程中的处理方式：
      deny_unknown_fields：在反序列化时，如果 JSON 中存在结构体未定义的字段，将返回错误。
      tag = "Mode"：这通常用于枚举类型，标记 JSON 中使用 Mode 作为标签，表明具体的枚举分支。
      rename_all = "PascalCase"：在序列化时，字段名将转换为 PascalCase（每个单词首字母大写，例如 MyField）
                                 以符合某些 API 的命名风格
*/
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
  feature = "serde",
  serde(deny_unknown_fields, tag = "Mode", rename_all = "PascalCase")
)]
pub enum ReplicationMode {
  /// Auto is used to automatically define that the number of replicas in the cluster
  /// This will ensure at least 1 replica exists in the cluster
  /// And automatically add more replicas in the cluster if needed for redundancy
  Auto,
  /// Unique is used to ensure that only one replica exists in the cluster
  Unique,
  /// UniqueByNode is used to ensure one replica is running on each node
  UniqueByNode,
  /// UniqueByNodeGroups is used to ensure one replica is running on each node group
  UniqueByNodeGroups { groups: Vec<String> },
  /// UniqueByNodeNames is used to ensure one replica is running on each node name
  UniqueByNodeNames { names: Vec<String> },
  /// Number is used to manually set the number of replicas in one node
  Static(ReplicationStatic),
  /// NumberByNodes is used to manually set the number of replicas in each node
  StaticByNodes(ReplicationStatic),
  /// NumberByNodeGroups is used to manually set the number of replicas in each node group
  StaticByNodeGroups { groups: Vec<String>, number: i64 },
  /// NumberByNodeNames is used to manually set the number of replicas in each node name
  StaticByNodeNames { names: Vec<String>, number: i64 },
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
  feature = "serde",
  serde(deny_unknown_fields, rename_all = "PascalCase")
)]
pub struct ReplicationStatic {
    // 手动设置副本数量
  pub number: usize,
}

/// A cargo spec partial is used to create a Cargo
#[derive(Debug, Default, Clone, PartialEq)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
  feature = "serde",
  serde(deny_unknown_fields, rename_all = "PascalCase")
)]
pub struct CargoSpecPartial {
  /// Name of the cargo
  pub name: String,
  /// Metadata of the cargo (user defined)
  /// #[cfg_attr(feature = "utoipa", schema(value_type = HashMap<String, Any>))]
  ///    该字段是一个动态键值对结构
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  #[cfg_attr(feature = "utoipa", schema(value_type = HashMap<String, Any>))]
  pub metadata: Option<String>,
  /// Action to run before the container
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub init_container: Option<ContainerConfig>,
  /// List of secrets to use as environment variables
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub secrets: Option<Vec<String>>,
  /// Secret to use when pulling the image
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub image_pull_secret: Option<String>,
  /// Image pull policy of the cargo
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub image_pull_policy: Option<ImagePullPolicy>,
  /// Container specification of the cargo
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub container:  Option<CriConfigPartial>,
  /// Replication specification of the cargo
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub replication: Option<ReplicationMode>,
}

/// Payload used to patch a cargo
/// It will create a new [CargoSpec](CargoSpec) with the new values
/// It will keep the old values in the history
#[derive(Debug, Default, Clone)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
  feature = "serde",
  serde(deny_unknown_fields, rename_all = "PascalCase")
)]
pub struct CargoSpecUpdate {
  /// New name of the cargo
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub name: Option<String>,
  /// New metadata of the cargo (user defined)
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  #[cfg_attr(feature = "utoipa", schema(value_type = HashMap<String, Any>))]
  pub metadata: Option<String>,
  /// Action to run before the container
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub init_container: Option<ContainerConfig>,
  /// List of secrets to use as environment variables
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub secrets: Option<Vec<String>>,
  /// Secret to use when pulling the image
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub image_pull_secret: Option<String>,
  /// Image pull policy of the cargo
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub image_pull_policy: Option<ImagePullPolicy>,
  /// New container specification of the cargo
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub container: Option<CriConfigPartial>,
  /// New replication specification of the cargo
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub replication: Option<ReplicationMode>,
}

impl From<CargoSpecPartial> for CargoSpecUpdate {
  fn from(spec: CargoSpecPartial) -> Self {
    Self {
      name: Some(spec.name),
      init_container: spec.init_container,
      container: spec.container,
      replication: spec.replication,
      metadata: spec.metadata,
      secrets: spec.secrets,
      image_pull_secret: spec.image_pull_secret,
      image_pull_policy: spec.image_pull_policy,
    }
  }
}

/// A cargo spec is the specification of a cargo
/// It used to know the state of the cargo
/// It keep tracking of an history when you patch an existing cargo
#[derive(Debug, Default, Clone)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "PascalCase"))]
pub struct CargoSpec {
  /// Unique identifier of the cargo spec
  pub key: String,
  /// The key of the cargo
  pub cargo_key: String,
  /// Version of the spec
  pub version: String,
  /// Creation date of the cargo spec
  pub created_at: String,
  /// Name of the cargo
  pub name: String,
  /// Metadata of the cargo (user defined)
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  #[cfg_attr(feature = "utoipa", schema(value_type = HashMap<String, Any>))]
  pub metadata: Option<String>,
  /// Action to run before the container
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub init_container: Option<ContainerConfig>,
  /// List of secrets to use as environment variables
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub secrets: Option<Vec<String>>,
  /// Secret to use when pulling the image
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub image_pull_secret: Option<String>,
  /// Image pull policy of the cargo
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub image_pull_policy: Option<ImagePullPolicy>,
  /// Container specification of the carg
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub container: Option<CriConfig>,
  /// Replication specification of the cargo
  #[cfg_attr(
    feature = "serde",
    serde(skip_serializing_if = "Option::is_none")
  )]
  pub replication: Option<ReplicationMode>,
}

impl From<CargoSpec> for CargoSpecPartial {
  fn from(spec: CargoSpec) -> Self {
    Self {
      init_container: spec.init_container,
      name: spec.name,
      replication: spec.replication,
      container: Some(CriConfigPartial::from_cri_config(spec.container.unwrap())),
      metadata: spec.metadata,
      secrets: spec.secrets,
      image_pull_secret: spec.image_pull_secret,
      image_pull_policy: spec.image_pull_policy,
    }
  }
}
